//Copyright 2012-2019 Keysight Technologies // //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at // //http://www.apache.org/licenses/LICENSE-2.0 // //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using OpenTap; namespace PluginDevelopment.Advanced_Examples { // This file shows an example of how to build an object that has more than usually complex data // and make it editable and serializable in a normal way. // There are more ways than this, but this is probably the simplest ways of making it. /// This settings element has some rather complex behaviors. For example it needs to know which DUT instance /// owns it. public class ComplexSettingsElement1 : INotifyPropertyChanged { /// /// In order to have the right AvailableInts, we need the DUT value to always be up to date. /// public IEnumerable AvailableInts => Dut?.AvailableInts ?? Enumerable.Empty(); int a; [AvailableValues(nameof(AvailableInts))] public int A { get => a; set { if (a == value) return; a = value; OnPropertyChanged(); } } int b; [AvailableValues(nameof(AvailableInts))] public int B { get => b; set { if (b == value) return; b = value; OnPropertyChanged(); } } internal ComplexSettingsExample1 Dut; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } [Display("Complex Settings DUT1", "Demonstrates how to use a list where items cannot be added or removed." + " Elements themselves can be changed, but they are also mostly read-only.", Groups: new[] { "Examples", "Plugin Development", "Advanced Examples" })] public class ComplexSettingsExample1 : Dut { public List AvailableInts { get; set; } NotifyList elements = new NotifyList(); public NotifyList Elements { get { elementsNotifyChanged(elements); elements.NotifyChanged = elementsNotifyChanged; elements.ElementChanged = onElementChanged; return elements; } set => elements = (value as NotifyList) ?? new NotifyList(value); } void onElementChanged(NotifyList list, ComplexSettingsElement1 element1) { // insert logic to handle changes in the element Log.Debug("Element {0} changed", list.IndexOf(element1) + 1); } void elementsNotifyChanged(NotifyList list) { foreach (var elem in list) elem.Dut = this; } } /// List type that is easily extendible. public class VirtualList : IList, IList { List elements = new List(); public virtual IEnumerator GetEnumerator() => elements.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public void Add(T item) => Insert(Count, item); public int Add(object value) { Add((T) value); return Count - 1; } public virtual bool Contains(object value) => ((IList)elements).Contains(value); public virtual void Clear() => elements.Clear(); public int IndexOf(object value) => IndexOf((T) value); public void Insert(int index, object value) => Insert(index, (T) value); public void Remove(object value) => Remove((T) value); public virtual bool Contains(T item) => elements.Contains(item); public virtual void CopyTo(T[] array, int arrayIndex) => elements.CopyTo(array, arrayIndex); public bool Remove(T item) { var idx = IndexOf(item); if (idx == -1) return false; RemoveAt(idx); return true; } public void CopyTo(Array array, int index) => CopyTo((T[]) array, index); public virtual int Count => elements.Count; public object SyncRoot => false; public bool IsSynchronized => false; public virtual bool IsReadOnly => ((IList) elements).IsReadOnly; public bool IsFixedSize => false; public virtual int IndexOf(T item) => elements.IndexOf(item); public virtual void Insert(int index, T item) => elements.Insert(index, item); public virtual void RemoveAt(int index) => elements.RemoveAt(index); object IList.this[int index] { get => this[index]; set => this[index] = (T) value; } public virtual T this[int index] { get => elements[index]; set => elements[index] = value; } } /// A type of list that can emit notifications on change. public class NotifyList : VirtualList { public NotifyList(IEnumerable initialElements = null) { foreach (var elem in initialElements ?? Enumerable.Empty()) Add(elem); } public NotifyList() { } // Invoked when the list has changed. public Action> NotifyChanged { get; set; } /// Invoked when an element of the list has changed. public Action, T> ElementChanged { get; set; } bool notify = true; void notifyOnChanged() { if(notify) NotifyChanged?.Invoke(this); } public override void Insert(int index, T item) { base.Insert(index, item); if (item is INotifyPropertyChanged n) n.PropertyChanged += elementPropertyChanged; notifyOnChanged(); } void elementPropertyChanged(object sender, PropertyChangedEventArgs e) => ElementChanged?.Invoke(this, (T)sender); public override T this[int index] { get => base[index]; set { notify = false; try { RemoveAt(index); } finally { notify = true; } Insert(index, value); } } public override void Clear() { base.Clear(); foreach (var item in this) { if (item is INotifyPropertyChanged n) n.PropertyChanged -= elementPropertyChanged; } notifyOnChanged(); } public override void RemoveAt(int index) { if (index >= Count) return; var elem = this[index]; if (elem is INotifyPropertyChanged n) n.PropertyChanged -= elementPropertyChanged; base.RemoveAt(index); notifyOnChanged(); } } }